package common;


import annotation.JwtCheck;
import enums.ResultCodeEnum;
import exception.AuthenticateException;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.config.ApplicationConfig;
import org.apache.dubbo.config.ReferenceConfig;
import org.apache.dubbo.config.RegistryConfig;
import org.apache.dubbo.config.annotation.DubboReference;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import service.JwtService;

import javax.servlet.http.HttpServletRequest;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;

/**
 * @program: pdd
 * @description: 用aop来处理JwtCheck注解
 * @create: 2020-11-15 14:08
 **/
@Component
@Aspect
public class JwtCheckAop {

    @DubboReference    //(url = "dubbo://127.0.0.1:8015")
    private JwtService jwtService;

    JwtCheckAop(){
        // 当前应用配置
        ApplicationConfig application = new ApplicationConfig();
        application.setName("jwt-check-consumer");

        // 连接注册中心配置
        RegistryConfig registry = new RegistryConfig();
        registry.setProtocol("nacos");
        registry.setAddress("121.199.45.1:8848");
        registry.setUsername("nacos");
        registry.setPassword("nacos");
        // 注意：ReferenceConfig为重对象，内部封装了与注册中心的连接，以及与服务提供方的连接
        // 引用远程服务
        ReferenceConfig<JwtService> reference = new ReferenceConfig<JwtService>(); // 此实例很重，封装了与注册中心的连接以及与提供者的连接，请自行缓存，否则可能造成内存和连接泄漏
        reference.setApplication(application);
        reference.setCheck(false);
        reference.setRegistry(registry); // 多个注册中心可以用setRegistries()
        reference.setInterface(JwtService.class);
        // 和本地bean一样使用userService
        this.jwtService = reference.get(); // 注意：此代理对象内部封装了所有通讯细节，对象较重，请缓存复用
    }

    @Pointcut("@annotation(annotation.JwtCheck)")
    private void apiAop(){
    }


    @Around("apiAop()")
    public Object aroundApi(ProceedingJoinPoint point) throws Throwable {
        Object proceed = null;
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        //获取参数上得所有注解
        Annotation[][] parameterAnnotationArray = method.getParameterAnnotations();
        Object[] args = point.getArgs();

        String token=null;
        String role=null;
        /*
           这个代码片得逻辑:找出有 @RequestHeader("Authorization") 的参数，赋值给 token变量,找出有 @JwtCheck("role") 的参数，赋值给 token变量
         */
        for(Annotation[]  annotations : parameterAnnotationArray){
            for(Annotation a:annotations){
                if(a instanceof RequestHeader){
                    RequestHeader requestHeader =  (RequestHeader)a;
                    if("Authorization".equals(requestHeader.value())){
                        token = (String) args[ArrayUtils.indexOf(parameterAnnotationArray,annotations)];
                        break;
                    }
                }
            }
        }

        /*
         检查role角色
         */
        if (method.isAnnotationPresent(JwtCheck.class)){
            System.out.println("========================\n检测到role\n================================");
            //获取方法上注解中表明的权限
            JwtCheck jwtCheck =method.getAnnotation(JwtCheck.class);
            role=jwtCheck.role();
        }
        /**
         * 获取http请求参数
         */
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
                .getRequest();
         token = request.getParameter("username");

        if(StringUtils.isBlank(role)){
            //没有role
            throw new AuthenticateException(ResultCodeEnum.ROLE_ISNULL);
        }
        if(StringUtils.isBlank(token)){
            //没有token
            throw new AuthenticateException(ResultCodeEnum.TOKEN_ISNULL);
        }else{
            //有token
            //验证token是否失效
            if(!jwtService.checkJwt(token,role))
                throw new AuthenticateException(ResultCodeEnum.LOGIN_EXPIRED);
        }
        // 执行目标方法
        try {
            proceed = point.proceed();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return proceed;

    }

}
